package cn.felord.security.autoconfigure;

import cn.felord.security.autoconfigure.util.AuthenticationUtil;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

import java.io.Serializable;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author felord.cn
 * @since 2021/8/6 14:37
 */
public class ResourcePermissionEvaluator implements PermissionEvaluator {
    private final AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
    private final PermissionService permissionService;

    public ResourcePermissionEvaluator(PermissionService permissionService) {
        this.permissionService = permissionService;
    }

    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {

        if (Objects.isNull(targetDomainObject)||Objects.isNull(permission)) {
            return false;
        }

        if (authenticationTrustResolver.isAnonymous(authentication)) {
            // 匿名用户不适用
            return false;
        }
        //   @PreAuthorize("hasPermission('user','add')") user:add
        final String permissionCode = targetDomainObject + ":" + permission;
        Set<String> rolesByPermission = permissionService.findRolesByPermission(permissionCode);
        Set<String> roles = AuthenticationUtil.currentUserRoles()
                .stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toSet());
        return roles.stream()
                .anyMatch(rolesByPermission::contains);
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        throw new UnsupportedOperationException("Not Support");
    }
}
